<?php
/***************************************************************************
 *   Copyright (C) 2007 by Andrea Blankenstijn, David Dieulivol            *
 *   darkan9el@gmail.com, daviddieulivol5@hotmail.com                      *
 *                                                                         *
 *   All rights reserved.                                                  *
 *   This work is released under the terms of the New BSD License.         *
 *   See LICENSE.BSD file for licensing details                            *
 ***************************************************************************/
/* * *
 * $Id: adminPhoto.class.php 55 2007-10-26 17:17:01Z DarkAn9el $
 * * */

/**
 * \class AdminPhoto
 * \version $Revision: 55 $
 * \date $Date: 2007-10-26 17:17:01 +0000 (Fri, 26 Oct 2007) $
 * \author Andrea Blankenstijn
 * \author David Dieulivol
 * \brief Classe d'administration concernant la gestion des images
 */

class AdminPhoto
{
	private $pdoProxy;     ///< instance de PdoProxy
	private $sha1sum;      ///< somme SHA1 du fichier
	private $type;         ///< extension du fichier
	private $tempFile;     ///< emplacement temporaire du fichier reçu
	private $tPhotos;      ///< nom de la table photos avec préfix

	/**
	 * \brief Constructeur de la classe
	 * \param[in] $dbh instance PDO
	 */
	public function __construct(PdoProxy $dbh)
	{
		$this->pdoProxy = $dbh;
		$this->tPhotos  = $dbh->getPrefix() .'photos';
	}

	/**
	 * \brief Méthode d'ajout d'une photo
	 * \param[in] $name nom de l'image
	 * \param[in] $description descriptif de l'image
	 * \param[in] $authorId identifiant idAuthors de l'auteur de l'image
	 * \param[in] $copyrightId identifiant idCopyrights du copyright de l'image
	 * \param[in] $file chemin de l'image à ajouter
	 * \param[in] $isOnline visible par le public ? (bool)
	 * \return $lastId identifiant idPhotos de l'image ajoutée
	 * \return FALSE si l'insertion à la DB à échouée sans lâcher d'exception (est-ce possible?)
	 * \exception Exception erreur durant de la transaction de la DB
	 * \exception Exception une image aillant la même somme SHA1 est déjà présente
	 */
	public function addPhoto($name, $description, $authorId, $copyrightId, $file, $isOnline = 0)
	{
		$this->storePhoto($file);

		$args = array(
			$name,
			$description,
			$this->sha1sum,
			$isOnline,
			$authorId,
			$this->type,
			$copyrightId
		);

		try
		{
			$this->pdoProxy->getDbh()->beginTransaction();

			$request = $this->pdoProxy->prepare('INSERT INTO `'.$this->tPhotos.'` '.
					'(`idPhotos`,`name`,`description`,`sha1Sum`,`isOnline`,`date`,`idAuthors`,`type`,`idCopyrights`) '.
					'VALUES '.
					'(NULL,:name,:desc,:sha1,:online,CURRENT_TIMESTAMP,:author,:type,:copyright);');

			$request->bindValue(':name', $name, PDO::PARAM_STR);
			$request->bindValue(':desc', $description, PDO::PARAM_STR);
			$request->bindValue(':sha1', $this->sha1sum, PDO::PARAM_STR);
			$request->bindValue(':online', $isOnline, PDO::PARAM_INT);
			$request->bindValue(':author', $authorId, PDO::PARAM_INT);
			$request->bindValue(':type', $this->type, PDO::PARAM_STR);
			$request->bindValue(':copyright', $copyrightId, PDO::PARAM_INT);

			$request->execute();
			
			if($request->rowCount() === 0)
				return FALSE;

			// Récupération de l'ID de l'image ajoutée
			$query  = $this->pdoProxy->query('SELECT MAX(`idPhotos`) FROM `'.$this->tPhotos.'`;');
			$lastId = $query->fetchColumn();
			$query->closeCursor();
	
			$this->pdoProxy->getDbh()->commit();
		}
		catch (Exception $e)
		{
			$this->pdoProxy->getDbh()->rollBack();
			throw new Exception('Erreur lors de la mise à jour de la base de données : '. $e->getMessage());
		}
	
		return $lastId;
	}

	/**
	 * \brief Méthode pour déterminer le type de fichier de la photo
	 * \return $ext l'extension du fichier selon son type
	 * \exception Exception aucune méthode utilisable pour déterminer le type du fichier
	 */
	private function checkType()
	{
		$ext = null;

		if(function_exists('exif_imagetype'))
			$ext = $this->checkTypeExif();

		// si exif_imagetype n'est pas disponible ou n'a pu déterminer le type de l'image
		if(empty($ext))
		{
			if(extension_loaded('fileinfo'))
				$mime = $this->checkTypeFinfo();
			elseif(function_exists('mime_content_type')) // deprecated
				$mime = checkTypeMimeContentType();
			elseif(!(CONF_SYS_FILE === FALSE))
				$mime = checkTypeSysFile();
			else
				throw new Exception('Aucune méthode pour déterminer le type de fichier n\'est disponible', 4);

			$ext = preg_replace('/^[a-z]*\/([a-z]*)/', '$1', $mime);

			// le type MIME donne jpeg, mais nous utilisons jpg
			if($ext == 'jpeg')
			$ext = 'jpg';
		}

		$this->type = $ext;
	}

	/**
	 * \brief Détection du type de l'image avec exif_imagetype()
	 *
	 * exif_imagetype() reconnait les principaux type d'images matricielles
	 * mais ne reconnais comme image vectoriel que celles en Flash.
	 *
	 * \return $ext l'extension de fichier du type
	 */
	private function checkTypeExif()
	{
		if(function_exists('image_type_to_extension'))
		{
			$type = exif_imagetype($this->tempFile);
			// substr nous débarasse du point précédant l'extension
			$ext  = substr(image_type_to_extension($type), 1);
		}
		else
		{
			$types = array(
				IMAGETYPE_GIF     => 'gif',
				IMAGETYPE_JPEG    => 'jpg',
				IMAGETYPE_PNG     => 'png',
				IMAGETYPE_SWF     => 'swf',  // Flash
				IMAGETYPE_PSD     => 'psd',
				IMAGETYPE_BMP     => 'bmp',
				IMAGETYPE_TIFF_II => 'tiff',
				IMAGETYPE_TIFF_MM => 'tiff',
				IMAGETYPE_JPC     => 'jpc',  // JPEG 2000
				IMAGETYPE_JP2     => 'jp2',  // JPEG 2000
				IMAGETYPE_JPX     => 'jpf',  // JPEG 2000
				IMAGETYPE_JB2     => 'jb2',  // JBIG2 Bitmap Graphic
				IMAGETYPE_SWC     => 'swc',  // Flash
				IMAGETYPE_IFF     => 'aiff',
				IMAGETYPE_WBMP    => 'wbmp', // Wireless Bitmap File Format (pour WAP)
				IMAGETYPE_XBM     => 'xbm'   // X Bitmap Graphic
			);

		$type = exif_imagetype($this->tempFile);
		$ext  = $types[$type];
		}

		return $ext;
	}

	/**
	 * \brief Détection du mimetype du fichier via l'extension Fileinfo
	 *
	 * cf. http://pecl.php.net/package/fileinfo
	 * \return $mime mimetype du fichier
	 * \exception Exception finfo_file a retourné false
	 */
	private function checkTypeFinfo()
	{
		$finfo = finfo_open(FILEINFO_MIME);
		$mime = finfo_file($finfo, $this->tempFile);
		finfo_close($finfo);

		if($mime === FALSE)
			throw new Exception('Fileinfo: erreur lors de l\'analyse du mimetype', 1);

		return $mime;
	}

	/**
	 * \brief Détection du mimetype du fichier via mime_content_type (deprecated)
	 * \return $mime mimetype du fichier
	 */
	private function checkTypeMimeContentType()
	{
		return mime_content_type($this->tempFile);
	}

	/**
	 * \brief Détection du mimetype du fichier via la commande système file
	 * \return $mime mimetype du fichier
	 * \exception Exception valeur de retour de file différente de 0
	 */
	private function checkTypeSysFile()
	{
		$cmdOutput   = array();
		$returnValue = 1;

		$mimetype    = exec('file -bi '. escapeshellarg($this->tempFile), $cmdOutput, $returnValue);

		$cmdOutput   = implode("\n", $cmdOutput);

		if($returnValue != 0)
			throw new Exception('file: la commande a retourné '. $returnValue ."\nsortie :\n". $cmdOutput, 3);
		else
			return $mimetype;
	}

	/**
	 * \brief Méthode de suppression d'une image
	 * \param[in] $photoIds des IDs des images à supprimer
	 * \return $count le nombre d'images supprimées
	 * \exception Exception pas d'entrée de la DB avec l'ID donnée
	 * \exception Exception erreur durant suppresion de l'entrée de la DB
	 * \exception Exception erreur lors de la suppression du fichier
	 */
	public function removePhoto(array $photoIds)
	{
		// compteur d'entrées supprimées
		$count  = 0;

		// données pour former le nom du fichier
		$select = $this->pdoProxy->prepare('SELECT `sha1Sum`,`type` FROM `'.$this->tPhotos.'` WHERE `idPhotos`=:id;');
		// suppression des données de la DB
		$tPhotos2Galleries = $this->pdoProxy->getPrefix() .'photos2galleries';
		$delete1           = $this->pdoProxy->prepare('DELETE FROM `'.$this->tPhotos.'` WHERE `idPhotos`=:id;');
		$delete2           = $this->pdoProxy->prepare('DELETE FROM `'.$tPhotos2Galleries.'` WHERE `idPhotos`=:id;');

		$select->bindParam(':id', $id, PDO::PARAM_INT);
		$delete1->bindParam(':id', $id, PDO::PARAM_INT);
		$delete2->bindParam(':id', $id, PDO::PARAM_INT);

		foreach($photoIds as $id)
		{
			$select->execute();
			if($row = $select->fetch())
			{
				$file = CONF_SYS_WORKDIR .'/'. CONF_IMG_PATH .'/'. $row['sha1Sum'] .'.'. $row['type'];
				$select->closeCursor();
			}
			else
				throw new Exception('Erreur lors de la recherche dans la base de données');

			// Suppression de la DB
			try
			{
				$this->pdoProxy->getDbh()->beginTransaction();
	
				$delete1->execute();
				$delete2->execute();
	
				$this->pdoProxy->getDbh()->commit();
			}
			catch (Exception $e)
			{
				$this->pdoProxy->getDbh()->rollBack();
				throw new Exception('Erreur lors de la suppression de la base de données : '. $e->getMessage());
			}

			// Suppression du fichier
			if(!unlink($file))
				throw new Exception('Erreur lors de la suppression du fichier '. $file);

			$count++;
		}

		return $count;
	}

	/**
	 * \brief Méthode de modification d'une image
	 * \param[in] $photoId l'ID de l'image
	 * \param[in] $name le nouveau nom de l'image
	 * \param[in] $description la nouvelle description
	 * \param[in] $authorId l'ID de nouvel auteur
	 * \param[in] $copyrightId l'ID du nouveau copyright
	 * \param[in] $isOnline si la photo est en ligne
	 * \return $count le nombre de lignes modifiées dans la DB (normallement 0 ou 1)
	 * \exception Exception erreur durant transaction de la DB
	 */
	public function modifyPhoto($photoId, $name = NULL, $description = NULL, $authorId = NULL, $copyrightId = NULL, $isOnline = NULL)
	{
		$requestString = 'UPDATE `'.$this->tPhotos.'` SET ';
		$requestArgs   = array();
		$i             = 0; // compteur pour savoir s'il faut mettre «,» ou non
    $isSetIsOnline = false;

		if(!empty($name))
		{
			$requestString .= '`name` = :name';
			$i++;
		}

		if(!empty($description))
		{
			if($i>0)
				$requestString .= ', ';

			$requestString .= '`description` = :desc';
			$i++;
		}

		if(!empty($authorId))
		{
			if($i>0)
				$requestString .= ', ';

			$requestString .= '`idAuthors` = :author';
			$i++;
		}

		if(!empty($copyrightId))
		{
			if($i>0)
				$requestString .= ', ';

			$requestString .= '`idCopyrights` = :copyright';
			$i++;
		}

		if(!($isOnline === NULL))
		{
			if($isOnline == 0)
			{
				if($i>0)
					$requestString .= ', ';
				$requestString .= '`isOnline` = 0';
				$i++;
			}
			elseif($isOnline > 0)
			{
				if($i>0)
					$requestString .= ', ';
				$requestString .= '`isOnline` = 1';
				$i++;
			}
		}

		// Si $i == 0, aucune modification à appliquer à la galerie, donc ne rien faire
		if($i>0)
		{
			$requestString .= ' WHERE `idPhotos` = :id;';

			try
			{
				$this->pdoProxy->getDbh()->beginTransaction();

				$request = $this->pdoProxy->prepare($requestString);

				$request->bindValue(':id', $photoId, PDO::PARAM_INT);
				if(!empty($name))          $request->bindValue(':name', $name, PDO::PARAM_STR);
				if(!empty($description))   $request->bindValue(':desc', $description, PDO::PARAM_STR);
				if(!empty($authorId))      $request->bindValue(':author', $authorId, PDO::PARAM_INT);
				if(!empty($copyrightId))   $request->bindValue(':copyright', $copyrightId, PDO::PARAM_INT);


				$request->execute();
				$count   = $request->rowCount();

				$this->pdoProxy->getDbh()->commit();
			}
			catch (Exception $e)
			{
				$this->pdoProxy->getDbh()->rollBack();
				throw new Exception('Erreur lors de la mise à jour de la base de données : '. $e->getMessage());
			}

			return $count;
		}
		else
		{
			return 0;
		}
	}

	/**
	 * \brief Déplace la photo dans le dossier défini
	 * dans le fichier de configuration
	 *
	 * \exception Exception Erreur lors du déplacement du fichier
	 * \return 0 terminé sans erreur
	 */
	private function movePhoto()
	{
		$dir       = CONF_SYS_WORKDIR .'/'. CONF_IMG_PATH;
		$photoPath = $dir .'/'. $this->sha1sum.'.'.$this->type;

		if(!copy($this->tempFile, $photoPath))
			throw new Exception('Erreur lors du déplacement du fichier reçu', 1);
		else
			return 0;
	}

	/**
	 * \brief Vérifie si une même image n'existe pas déjà
	 * Vérifie si une image ayant la même somme SHA1 que celle passée
	 * en argument n'est pas déjà présente dans la base de donnée
	 * \param[in] $sha1 somme SHA1 à chercher
	 * \return $photoId identifiant de l'entrée de la table photos
	 * correspondant à la somme $sha1
	 * \return FALSE aucune entrée ne correspond à la somme $sha2
	 */
	private function photoExists($sha1)
	{
		$request = $this->pdoProxy->prepare('SELECT `idPhotos`  FROM `'.$this->tPhotos.'` WHERE `sha1Sum`=:sha1');
		$request->bindValue(':sha1', $sha1, PDO::PARAM_STR);
		$request->execute();

		if($photoId = $request->fetchColumn())
			return $photoId;
		else
			return FALSE;
	}

	/**
	 * \brief Déplace et renomme le fichier uploadé
	 * \param[in] $file chemin du fichier à traiter
	 * \exception Exception lâche une exception si
	 * AdminPhoto::photoExists($this->sha1sum) est
	 * différent de FALSE
	 */
	private function storePhoto($file)
	{
		$this->tempFile  = $file;
		$this->sha1sum = sha1_file($file);

		if(!$this->photoExists($this->sha1sum))
		{
			$this->checkType();
			$this->movePhoto();
		}
		else
			throw new Exception('Une insertion identique existe déjà dans la base de données');
	}
}
?>
